/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.wsinfra.wse.impl;

import java.util.Calendar;
import java.util.Hashtable;

import cgl.narada.wsinfra.WsMessageFlow;
import cgl.narada.wsinfra.WsProcessor;
import cgl.narada.wsinfra.exception.DeploymentException;
import cgl.narada.wsinfra.exception.IncorrectExchangeException;
import cgl.narada.wsinfra.exception.MessageFlowException;
import cgl.narada.wsinfra.exception.ParsingException;
import cgl.narada.wsinfra.exception.ProcessingException;
import cgl.narada.wsinfra.exception.UnknownExchangeException;
import cgl.narada.wsinfra.exception.WsFaultException;
import cgl.narada.wsinfra.schemas.addressing.ActionDocument;
import cgl.narada.wsinfra.schemas.addressing.EndpointReferenceType;
import cgl.narada.wsinfra.schemas.eventing.FilterType;
import cgl.narada.wsinfra.schemas.eventing.GetStatusDocument;
import cgl.narada.wsinfra.schemas.eventing.GetStatusResponseDocument;
import cgl.narada.wsinfra.schemas.eventing.IdentifierDocument;
import cgl.narada.wsinfra.schemas.eventing.RenewDocument;
import cgl.narada.wsinfra.schemas.eventing.RenewResponseDocument;
import cgl.narada.wsinfra.schemas.eventing.SubscribeDocument;
import cgl.narada.wsinfra.schemas.eventing.SubscribeResponseDocument;
import cgl.narada.wsinfra.schemas.eventing.SubscriptionEndDocument;
import cgl.narada.wsinfra.schemas.soap.EnvelopeDocument;
import cgl.narada.wsinfra.wsa.AddressingHeaders;
import cgl.narada.wsinfra.wsa.ParseWsaHeaders;
import cgl.narada.wsinfra.wsa.WsaEprRules;
import cgl.narada.wsinfra.wsa.WsaFaultRules;
import cgl.narada.wsinfra.wsa.WsaProcessingFactory;
import cgl.narada.wsinfra.wse.WseActions;
import cgl.narada.wsinfra.wse.WseElementCreation;
import cgl.narada.wsinfra.wse.WseFaults;
import cgl.narada.wsinfra.wse.WseNodeUtils;
import cgl.narada.wsinfra.wse.WseProcessingFactory;
import cgl.narada.wsinfra.wse.WseQNames;
import cgl.narada.wsinfra.wse.WseRequestCreator;
import cgl.narada.wsinfra.wse.matching.SubscriptionEntry;
import cgl.narada.wsinfra.wse.matching.SubscriptionEntryFactory;
import cgl.narada.wsinfra.wse.matching.SubscriptionExpiryListener;
import cgl.narada.wsinfra.wse.matching.SubscriptionManagement;
/** This class which implements the interface that encapsulates the behavior
    of a WS-Eventing sink node.

    @author Shrideep Pallickara
    $Date: 2005/07/29 22:41:30 $
    $Revision: 1.7 $
*/

public class WseSinkProcessor extends WsProcessor 
  implements SubscriptionExpiryListener {

  private static final WseSinkProcessor instance = new WseSinkProcessor();
  private EndpointReferenceType sinkEpr;
  
  private ParseWsaHeaders parseWsaHeaders;
  private WsaFaultRules wsaFaultRules;
  private WsaEprRules wsaEprRules;
 
  private WseActions wseActions;
  private WseQNames wseQNames;
  private WseFaults wseFaults;
  private WseNodeUtils wseNodeUtils;

  private WseRequestCreator wseRequestCreator;
  private WseElementCreation wseElementCreation;
  
  private SubscriptionManagement subscriptionManagement;
  private SubscriptionEntryFactory subscriptionEntryFactory;

  private WsMessageFlow wsMessageFlow;
  private Hashtable subscribeRequests, renewRequests, 
    getStatusRequests, unsubscribeRequests;
  private String moduleName = "WseSinkProcessor: ";

  private WseSinkProcessor() {
    parseWsaHeaders = WsaProcessingFactory.getParseWsaHeaders();
    wsaFaultRules =  WsaProcessingFactory.getWsaFaultRules();
    wsaEprRules = WsaProcessingFactory.getWsaEprRules();

    wseActions = WseActions.getInstance();
    wseQNames = WseQNames.getInstance();
    wseFaults = WseFaults.getInstance();
    wseNodeUtils = WseProcessingFactory.getWseNodeUtils();
    wseRequestCreator = WseProcessingFactory.getWseRequestCreator();
    wseElementCreation = WseProcessingFactory.getWseElementCreation();

    subscriptionManagement = new SubscriptionManagement();
    subscriptionEntryFactory = SubscriptionEntryFactory.getInstance();

    subscribeRequests = new Hashtable();
    renewRequests = new Hashtable();
    getStatusRequests = new Hashtable();
    unsubscribeRequests = new Hashtable();
  }

  public static WseSinkProcessor getInstance() {
    return instance;
  }
  
  /**
   * Retrieves the subscription management 
   * @return SubscriptionManagement
   */
  public SubscriptionManagement getSubscriptionManagement() {
	  return subscriptionManagement;
  }

  
  /** Sets the Sink EPR */
  public void setSinkEPR(EndpointReferenceType sinkEpr) {
    this.sinkEpr = sinkEpr;
  }

  /** Sets the message flow which the processor should use */
  public void setMessageFlow(WsMessageFlow wsMessageFlow) 
    throws DeploymentException {
    this.wsMessageFlow = wsMessageFlow;
  }
  
  /** Gets the message flow which the processor should use. */
  public WsMessageFlow getMessageFlow() {
    return wsMessageFlow;
  }


  /** Process the exchange. The argument also indicates the direction in
      which the exchange has actually traversed. */
  public boolean processExchange(EnvelopeDocument envelopeDocument,
				 int direction)
    throws UnknownExchangeException, IncorrectExchangeException,
    MessageFlowException, ProcessingException {

     AddressingHeaders addressingHeaders;
    try {
      addressingHeaders =
	parseWsaHeaders.getAddressingHeaders(envelopeDocument);
    } catch (ParsingException parseEx) {
      throw new ProcessingException(moduleName + "Unable to process. " +
				    "Problems related to parsing");
    }

    ActionDocument actionDocument = addressingHeaders.getAction();
    String action = null;
    if (actionDocument != null) {
      action = actionDocument.getAction().getStringValue();
    }
    
    checkExchangeType(action, direction);

    /** Only valid exchanges to be processed now remain */
    if (action == null) {
      processNotification(envelopeDocument, addressingHeaders);
      return true;
    }    

    try {
      if (action.equals(wseActions.getSubscribe()) ) {
	storeRequestInfo(envelopeDocument, subscribeRequests);
	return true;
      }


      if (action.equals(wseActions.getUnsubscribe()) ) {
	storeRequestInfo(envelopeDocument, unsubscribeRequests);
	return true;
      }


      if (action.equals(wseActions.getGetStatus()) ) {
	storeRequestInfo(envelopeDocument, getStatusRequests);
	return true;
      }


      if (action.equals(wseActions.getRenew()) ) {
	storeRequestInfo(envelopeDocument, renewRequests);
	return true;
      } 



      /** Process the subscribe request */
      if (action.equals(wseActions.getSubscribeResponse()) ) {
	processSubscribeResponse(envelopeDocument, addressingHeaders);
      } 
      
      /** Process renewal response. */
      if (action.equals(wseActions.getRenewResponse()) ) {
	processRenewResponse(envelopeDocument, addressingHeaders);
      }
      
      
      /** Process the getStatus response. */
      if (action.equals(wseActions.getGetStatusResponse()) ) {
	processGetStatusResponse(envelopeDocument, addressingHeaders);
      }
      
      /** Process Unsubscribe . */
      if (action.equals(wseActions.getUnsubscribeResponse()) ) {
	processUnsubscribeResponse(envelopeDocument, addressingHeaders);
      }

      /** Process SubscriptionEnd . */
      if (action.equals(wseActions.getSubscriptionEnd()) ) {
	processSubscriptionEnd(envelopeDocument, addressingHeaders);
      }

    } catch (WsFaultException wsFaultException ) {
       EndpointReferenceType faultTo =
	 wsaFaultRules.sendFaultTo(addressingHeaders);
       wsFaultException.setFaultTo(faultTo);
       manageFaultException(wsFaultException, direction);
       System.out.println(moduleName + wsFaultException);
    }
    
    return false;
  }


  /** Creates a subscribe request based on the specified parameters. */
  public EnvelopeDocument
  createSubscribeRequest(EndpointReferenceType sourceEpr,
			 String deliveryMode, EndpointReferenceType endTo,
			 String filterDialect, String filterConstraint,
			 Calendar expiresAt) 
    throws ProcessingException {
    SubscribeDocument subscribeDocument =
      wseElementCreation.newSubscribeDocument(deliveryMode, endTo,
					      filterDialect,filterConstraint,
					      expiresAt, sinkEpr);
    EnvelopeDocument envelopeDocument =
      wseRequestCreator.createSubscribe(sourceEpr, sinkEpr, subscribeDocument);

    storeRequestInfo(envelopeDocument, subscribeRequests);
    
    return envelopeDocument;
  }


  
  public EnvelopeDocument 
  createGetStatus(String subscriptionIdentifier) 
    throws ProcessingException {

    SubscriptionEntry subscriptionEntry = 
      retriveSubscriptionEntry(subscriptionIdentifier);
    EndpointReferenceType subcriptionManagerEpr = 
      subscriptionEntry.getSubscriptionManager();

    GetStatusDocument getStatusDocument= 
      wseElementCreation.newGetStatusDocument();
    
    
    
    
    EnvelopeDocument envelopeDocument = 
      wseRequestCreator.createGetStatus(subcriptionManagerEpr, sinkEpr, 
					getStatusDocument);

    storeRequestInfo(envelopeDocument, getStatusRequests);
    return envelopeDocument;
  }

  
  public EnvelopeDocument
  createRenew(String subscriptionIdentifier, Calendar renew) 
  throws ProcessingException {
    
    SubscriptionEntry subscriptionEntry = 
      retriveSubscriptionEntry(subscriptionIdentifier);
     EndpointReferenceType subcriptionManagerEpr = 
      subscriptionEntry.getSubscriptionManager();

    RenewDocument renewDocument = wseElementCreation.newRenewDocument(renew);
    
    EnvelopeDocument envelopeDocument = 
      wseRequestCreator.createRenew(subcriptionManagerEpr, sinkEpr,
				    renewDocument);

    storeRequestInfo(envelopeDocument, renewRequests);
    return envelopeDocument;
  }


  public EnvelopeDocument 
  createUnsubscribe(String subscriptionIdentifier) throws ProcessingException {
     
    SubscriptionEntry subscriptionEntry = 
      retriveSubscriptionEntry(subscriptionIdentifier);
    EndpointReferenceType subcriptionManagerEpr = 
       subscriptionEntry.getSubscriptionManager();
    
    EnvelopeDocument envelopeDocument = 
      wseRequestCreator.createUnsubscribe(subcriptionManagerEpr, sinkEpr);
    
    storeRequestInfo(envelopeDocument, unsubscribeRequests);
    return envelopeDocument;
  }
  

  /** This methode retrievs the subscription entry associated with the 
      subscription identifier. */
  private SubscriptionEntry 
  retriveSubscriptionEntry(String subscriptionIdentifier)
    throws ProcessingException {
    String errorReport = "";
    if (subscriptionIdentifier == null) {
      errorReport = moduleName + "Specified subscription identifier is NULL!";
      throwProcessingException(errorReport);
    }

    if (!subscriptionManagement.hasEntry(subscriptionIdentifier)) {
      errorReport = moduleName + "Specified subscription identifier [" +
	subscriptionIdentifier + "] is NOT KNOWN to the WSE SINK NODE.";
      throwProcessingException(errorReport);
    }
    
    SubscriptionEntry subscriptionEntry =
      subscriptionManagement.getEntry(subscriptionIdentifier);

    return subscriptionEntry;
  }



  /** Add information regarding a given request into the appropriate table. */
  private void storeRequestInfo(EnvelopeDocument envelopeDocument,
				Hashtable requestsTable) 
    throws ProcessingException {
    if (envelopeDocument == null || requestsTable == null) {
      System.out.println(moduleName + "Either the created REQUEST Envelope" +
			 " is NULL or the specified requestsTable is NULL.");
      return;
    }

    AddressingHeaders addressingHeaders;

    try {
      addressingHeaders =
	parseWsaHeaders.getAddressingHeaders(envelopeDocument);
    } catch (ParsingException parseEx) {
      throw new ProcessingException(moduleName + "Unable to process. " +
				    "Problems related to parsing");
    }
    
    String messageIdOfRequest = 
      addressingHeaders.getMessageID().getMessageID().getStringValue();

    requestsTable.put(messageIdOfRequest, envelopeDocument);

  }
  
  /** Retrieve the envelope associated with the requestId from the requestsTable
      that is associated with requests. */
  private EnvelopeDocument 
  retrieveRequestEnvelope(Hashtable requestsTable, String messageIdOfRequest,
			  String requestType) 
    throws WsFaultException {
    String faultReason = "";
    if (requestsTable == null || messageIdOfRequest == null ) {
      System.out.println(moduleName + "Specified requestsTable for " +
			 requestType + "(s) is NULL!");
      return null;
    }
    
    if (!requestsTable.containsKey(messageIdOfRequest)) {
      faultReason = "The SINK node is NOT aware of making a " + requestType + 
	"Request, based on the [wsa:RelatesTo] identifier [" + 
	messageIdOfRequest + "] that is contained in the " + requestType + 
	"Response. Please check this Response again.\n";
      throwInvalidMessageFault(faultReason);
    }
    
    EnvelopeDocument requestEnvelope = 
      (EnvelopeDocument) requestsTable.get(messageIdOfRequest);
    
    return requestEnvelope;
  }

  /** This method is used to ensure process notifications. */
  public void processNotification(EnvelopeDocument envelopeDocument,
				  AddressingHeaders addressingHeaders)
    throws ProcessingException {


  }



  /** This method is used to process SubscribeResponses. */
  public void processSubscribeResponse(EnvelopeDocument envelopeDocument,
				       AddressingHeaders addressingHeaders ) 
  throws WsFaultException, ProcessingException {
    
    System.out.println(moduleName + "Received subscribe response");
    String messageIdOfSubscribeRequest = 
      checkRelatesToForProblems(addressingHeaders);
    
    String requestType = "Subscribe";
    EnvelopeDocument subscribeEnvelope = 
      retrieveRequestEnvelope(subscribeRequests, messageIdOfSubscribeRequest,
			      requestType);

    SubscribeDocument subscribeDocument = 
      wseNodeUtils.getSubscribeDocument(subscribeEnvelope);

    SubscribeResponseDocument subscribeResponseDocument =
      wseNodeUtils.getSubscribeResponseDocument(envelopeDocument);
   
    /** The identifier for this subscription would be contained in the
	subscribeResponse which is contained within the envelope body. */
    IdentifierDocument idDocument =
      wseNodeUtils.getIdentifierDocumentFromBody(envelopeDocument);
    
    String subscriptionId = idDocument.getIdentifier();
    if (subscriptionId == null) {
      String faultReason = "No subscription identifier has been specified " +
	"OR it is NULL. Please check your request again.";
      throwInvalidMessageFault(faultReason);
    }
    
    SubscribeDocument.Subscribe subscribe = subscribeDocument.getSubscribe();
    FilterType filter = subscribe.getFilter();
    
    SubscribeResponseDocument.SubscribeResponse subscribeResponse =
      subscribeResponseDocument.getSubscribeResponse();
    Object expiresAtObject = subscribeResponse.getExpires(); 
    Calendar expiresAt = null;
    if (expiresAtObject != null) {
      expiresAt = (Calendar) expiresAtObject;
    }

    EndpointReferenceType notifyTo=sinkEpr;
    EndpointReferenceType endTo = null;
     boolean hasEndTo = subscribe.isSetEndTo();
    if (hasEndTo) {
      endTo = subscribe.getEndTo();
    }
    
    EndpointReferenceType sourceEpr = wsaEprRules.sendTo(addressingHeaders);
    EndpointReferenceType subscriptionManagerEpr =
      subscribeResponse.getSubscriptionManager();
    
    SubscriptionEntry subscribeEntry = 
      subscriptionEntryFactory.createEntry(subscriptionId, filter, expiresAt,
					   sourceEpr, notifyTo, endTo,
					   subscriptionManagerEpr);
    
    subscriptionManagement.addEntry(subscribeEntry);

  }


  /** This method is used to process RenewResponses. */
  public void processRenewResponse(EnvelopeDocument envelopeDocument,
				   AddressingHeaders addressingHeaders ) 
    throws WsFaultException, ProcessingException {
    String messageIdOfRenewRequest = 
      checkRelatesToForProblems(addressingHeaders);

    

    RenewResponseDocument renewResponseDocument =
      wseNodeUtils.getRenewResponseDocument(envelopeDocument);
    RenewResponseDocument.RenewResponse renewResponse 
      = renewResponseDocument.getRenewResponse();

    String requestType = "Renew";
    EnvelopeDocument renewEnvelope = 
      retrieveRequestEnvelope(renewRequests, messageIdOfRenewRequest, 
			      requestType);
    IdentifierDocument idDocument =
      wseNodeUtils.getIdentifierDocument(renewEnvelope);
    String subscriptionId = idDocument.getIdentifier();
    System.out.println(moduleName + "Subscription identifier with request =>"
		       + subscriptionId);
    checkSubscriptionIdentifierForProblems(subscriptionId);
    
    SubscriptionEntry subscriptionEntry = 
      subscriptionManagement.getEntry(subscriptionId);
    
    if (renewResponse.isSetExpires()) {
      Calendar renewal = (Calendar) renewResponse.getExpires();
      subscriptionEntryFactory.updateSubscriptionEntry(subscriptionEntry, 
						       renewal);
    }

  }



  /** This method is used to process GetStatusRsponses. */
  public void processGetStatusResponse(EnvelopeDocument envelopeDocument,
				       AddressingHeaders addressingHeaders ) 
    throws WsFaultException, ProcessingException {
    
    String messageIdOfGetStatusRequest = 
      checkRelatesToForProblems(addressingHeaders);
    String requestType = "GetStatus";
    EnvelopeDocument getStatusEnvelope = 
      retrieveRequestEnvelope(getStatusRequests, messageIdOfGetStatusRequest,
			      requestType);

    IdentifierDocument idDocument =
      wseNodeUtils.getIdentifierDocument(getStatusEnvelope);
    
    String subscriptionId = idDocument.getIdentifier();
    checkSubscriptionIdentifierForProblems(subscriptionId); 
    
    GetStatusResponseDocument getStatusResponseDocument =
      wseNodeUtils.getGetStatusResponseDocument(envelopeDocument);
    GetStatusResponseDocument.GetStatusResponse getStatusResponse =
      getStatusResponseDocument.getGetStatusResponse();
    
    System.out.println(moduleName + "Status of subscription [" + subscriptionId
		       + " ]. The subscription will expires at" + 
		       getStatusResponse.getExpires() );
  }


  /** This method is used to process UnsubscribeResponses. */
  public void processUnsubscribeResponse(EnvelopeDocument envelopeDocument,
					 AddressingHeaders addressingHeaders)
    throws WsFaultException {
    String faultReason = "";
    String messageIdOfUnsubscribeRequest = 
      checkRelatesToForProblems(addressingHeaders);
    String requestType = "Unsubscribe";
    EnvelopeDocument unsubscribeEnvelope = 
      retrieveRequestEnvelope(unsubscribeRequests, 
			      messageIdOfUnsubscribeRequest, requestType);

    IdentifierDocument idDocument =
      wseNodeUtils.getIdentifierDocument(unsubscribeEnvelope);
    
    String subscriptionId = idDocument.getIdentifier();
    checkSubscriptionIdentifierForProblems(subscriptionId); 
    System.out.println(moduleName + "Trying to unsubscribe subscriptionID =>"
		       + subscriptionId);
    
    SubscriptionEntry subscriptionEntry =
      subscriptionManagement.deleteEntry(subscriptionId);    
  }



   /** This method is used to process RenewResponses. */
  public void processSubscriptionEnd(EnvelopeDocument envelopeDocument,
				     AddressingHeaders addressingHeaders ) 
    throws WsFaultException, ProcessingException {
    SubscriptionEndDocument subscriptionEndDocument =
      wseNodeUtils.getSubscriptionEndDocument(envelopeDocument);
    SubscriptionEndDocument.SubscriptionEnd subscriptionEnd =
      subscriptionEndDocument.getSubscriptionEnd();

    IdentifierDocument idDocument =
      wseNodeUtils.getIdentifierDocument(envelopeDocument);
    
    String subscriptionId = idDocument.getIdentifier();
    checkSubscriptionIdentifierForProblems(subscriptionId); 
    System.out.println(moduleName + "Received a SubscriptionEnd from SOURCE for"
		       + " Subscription Identifier [" + subscriptionId + "]" +
		       " The termination reason is => "+ 
		       subscriptionEnd.getReasonArray(0));
    
    SubscriptionEntry subscriptionEntry =
      subscriptionManagement.deleteEntry(subscriptionId);
    
  }

  /** Method that is called when one of the managed subscriptions expires */
  public void onSubscriptionExpiry(SubscriptionEntry expiredEntry) {
    if (expiredEntry == null) {
      System.out.println(moduleName + 
			 "Trying to expire a NULL subscription entry");
      return;
    }

    String subscriptionId = expiredEntry.getIdentifier();
    String status = 
      "http://schemas.xmlsoap.org/ws/2004/08/eventing/SubscriptionExpiry";
    String reason = "The subscription [" + subscriptionId + "] has expired!";

  }

  
  /** This method checks the relatesTo element for problems, issues faults
      if there are problems and returns the [wsa:relatesTo] value if there
      are NO problems. */
  private String 
  checkRelatesToForProblems(AddressingHeaders addressingHeaders)
    throws WsFaultException {
    String faultReason = "";
    
    if (!addressingHeaders.hasRelatesTo()) {
      faultReason = "No [wsa:RelatesTo] identifier has been specified in the "
	+ "exchange. There is thus no way to confirm if this message is indeed"
	+ "a RESPONSE to a PREVIOUSLY issued REQUEST. " +
	"Please check your response again.\n";
      throwInvalidMessageFault(faultReason);
    }

    String relatesTo = 
      addressingHeaders.getRelatesTo().getRelatesTo().getStringValue();
    if (relatesTo == null) {
      faultReason = "The [wsa:RelatesTo] identifier specified in the exchange"
	+ "is NULL! There is thus no way to confirm if this message is indeed"
	+ "a RESPONSE to a PREVIOUSLY issued REQUEST. " +
	"Please check your response again.\n";
      throwInvalidMessageFault(faultReason);
    }

    return relatesTo;
  }


  /** Check the specified subscription identifier for problems. Specifically
      throw an wse:InvalidFault if there are problems. */
  private void  
  checkSubscriptionIdentifierForProblems(String subscriptionIdentifier)
    throws WsFaultException {
    String faultReason = "";

    if (subscriptionIdentifier == null) {
      faultReason = "No subscription identifier has been specified or it is" +
	" NULL. Please check your request again.";
      throwInvalidMessageFault(faultReason);
    }

    if (!subscriptionManagement.hasEntry(subscriptionIdentifier)) {
      faultReason = "The specified subscription identifier [" +  
	subscriptionIdentifier + "] is not known to the SubscriptionManager."+
	" Please check your request again.";
      throwInvalidMessageFault(faultReason);
    }
  }

  /** Throw a WsFaultException, initialized with the invalid message QName
      and the specified reason */
  private void throwInvalidMessageFault(String reason)
    throws WsFaultException {

    throw
      new WsFaultException(wseFaults.getInvalidMessage(), reason);
  }


  
  private void checkExchangeType(String action, int direction)
    throws UnknownExchangeException, IncorrectExchangeException {

    if (direction == WsMessageFlow.FROM_NETWORK) {
      checkExchangeFromNetwork(action);
    }

    if (direction == WsMessageFlow.FROM_APPLICATION) {
      checkExchangeFromApplication(action);
    }

  }


  private void checkExchangeFromNetwork(String action)
    throws UnknownExchangeException, IncorrectExchangeException {
    String from = "Network. ";
    if (action == null) {
      //throwUnknownExchangeException(action, from);
      return;
    }

    if (action.equals(wseActions.getSubscribe()) ||
	action.equals(wseActions.getGetStatus()) ||
	action.equals(wseActions.getRenew()) ) {
      throwIncorrectExchangeException(action, from);
    }

  }


  private void checkExchangeFromApplication(String action)
    throws UnknownExchangeException, IncorrectExchangeException {
    String from = "Application. ";
    if (action == null) {
      throwUnknownExchangeException(action, from);
      //return;
    }

    if (//action.equals(wseActions.getSubscribe()) ||
	//action.equals(wseActions.getUnsubscribe()) ||
	action.equals(wseActions.getSubscribeResponse()) ||
	//action.equals(wseActions.getGetStatus()) ||
	action.equals(wseActions.getGetStatusResponse()) ||
	//action.equals(wseActions.getRenew()) ||
	action.equals(wseActions.getRenewResponse()) ) {
      throwIncorrectExchangeException(action, from);
    }
  }


  private void throwIncorrectExchangeException(String action, String from)
    throws IncorrectExchangeException {
    String reason =  moduleName +
      "Should NOT have received exchange corresponding to action [" +
      action + "] from the " + from;
    throw new IncorrectExchangeException(reason);
  }


  private void throwUnknownExchangeException(String action, String from)
    throws UnknownExchangeException {
    String reason =  moduleName +
      "No idea about processing exchange corresponding to action [" +
      action + "] from the " + from + ".";
    throw new UnknownExchangeException(reason);
  }


  private void throwProcessingException(String reason) 
    throws ProcessingException {
    ProcessingException pe = new ProcessingException(reason);
    throw pe;
  }
}

